package src.Players;

import java.io.*;
import java.net.Socket;
import src.Packets.*;
import src.Util.*;
import src.GameEngine;
import src.Main;
import src.NPCs.*;
import src.World.*;
import src.SQL;

public class Player extends PlayerInstance
{
    private Main main;
    public Frames frame;
    private PlayerUpdate updateP;
    public InputStream in;
    public SQL sql;
    public OutputStream out;
    public Socket socket;
    public Stream inStream = null, outStream = null;
    public Cryption inStreamDecryption = null;

    public Player(Socket s, int index)
    {
        playerIndex = index;
        socket = s;
        try
        {
            in = s.getInputStream();
            out = s.getOutputStream();
        }
        catch(IOException ioe)
        {
            ioe.printStackTrace();
            disconnected[0] = disconnected[1] = true;
            return;
        }
	/* if(sql.Load(password)) {
	    disconnected[0] = disconnected[1] = true;
	    return;
	} */
        main = Main.m;
        frame = main.ge.frame;
        updateP = main.ge.updateP;
        outStream = new Stream(new byte[getBufferSize()]);
        inStream = new Stream(new byte[getBufferSize()]);
        if(!main.ge.pkt.login(this))
        {
            disconnected[0] = disconnected[1] = true;
            return;
        }
        main.log(username + " has logged in.");
        loginSuccess();
    }

    public void applyDead()
    {
        if(deathDelay == 2)
        {
            if(faceToRequest != 65535)
            {
                requestFaceTo(65535);
            }
            requestAnim(2304, 0);
        }
        if(deathDelay <= 0)
        {
            dropAllItems(getPlayerKiller());
            frame.sendMessage(this, "Oh dear you are dead!");
            isDead = false;
            teleportToX = 3243;
            teleportToY = 3518;
            for(int i = 0; i < skillLvl.length; i++)
            {
                skillLvl[i] = getLevelForXP(i);
                frame.sendSkillLvl(this, i);
            }
            canWalkDelay = 0;
            subtractDamage(999999);
            requestAnim(-1, 0);
        }
    }

    public void dropAllItems(int playerGetId)
    {
        if(playerGetId <= 0 || main.ge.players[playerGetId] == null)
        {
            playerGetId = playerIndex;
        }
        Player plr = main.ge.players[playerGetId];
        PlayerItems pi = new PlayerItems();
        if(playerGetId != playerIndex)
        {
            plr.frame.sendMessage(plr, "You have defeated "+username+"!");
        }
        main.ge.item.createGroundItem(526, 1, absX, absY, heightLevel, plr.username);
        for(int i = 0; i < items.length; i++)
        {
            if(items[i] > 0)
            {
                if(main.ge.item.isUntradable((items[i] - 1)))
                {
                    main.ge.item.createGroundItem(items[i] - 1, itemsN[i], absX, absY, heightLevel, username);
                    pi.deleteItem(this, items[i] - 1, pi.getItemSlot(this, items[i] - 1), itemsN[i]);
                }
                else
                {
                    main.ge.item.createGroundItem(items[i] - 1, itemsN[i], absX, absY, heightLevel, plr.username);
                    pi.deleteItem(this, items[i] - 1, pi.getItemSlot(this, items[i] - 1), itemsN[i]);
                }
            }
        }
        for(int i = 0; i < equipment.length; i++)
        {
            if(equipment[i] > 0)
            {
                frame.removeEquipment(this, equipment[i], i);
                if(main.ge.item.isUntradable((items[0] - 1)))
                {
                    main.ge.item.createGroundItem(items[0] - 1, itemsN[0], absX, absY, heightLevel, username);
                    pi.deleteItem(this, items[0] - 1, pi.getItemSlot(this, items[0] - 1), itemsN[0]);
                }
                else
                {
                    main.ge.item.createGroundItem(items[0] - 1, itemsN[0], absX, absY, heightLevel, plr.username);
                    pi.deleteItem(this, items[0] - 1, pi.getItemSlot(this, items[0] - 1), itemsN[0]);
                }
            }
        }
        pi = null;
    }

    public int getPlayerKiller()
    {
        int Killer = 0;
        int Count = 0;
        for(int i = 1; i < main.ge.maxPlayers; i++)
        {
            if(Killer == 0)
            {
                Killer = i;
                Count = 1;
            }
            else
            {
                if(killedBy[i] > killedBy[Killer])
                {
                    Killer = i;
                    Count = 1;
                }
                else if(killedBy[i] == killedBy[Killer])
                    Count++;
            }
        }
        if(Count > 1)
            Killer = playerIndex;
        return Killer;
    }

    public void subtractDamage(int dmg)
    {
        for(int i = 1; i < killedBy.length; i++)
        {
            killedBy[i] -= dmg;
            if(killedBy[i] < 0)
                killedBy[i] = 0;
        }
    }

    public void process()
    {
        /*Updates every 600 ms*/
        if(loadingDelay >= 0)
        {
            loadingDelay--;
        }
        if(saveGameDelay > 0)
        {
            saveGameDelay--;
        }
        if(canWalkDelay > 0)
        {
            canWalkDelay--;
        }
        if(combatDelay > 0)
        {
            combatDelay--;
        }
        if(eatDelay > 0)
        {
            eatDelay--;
        }
        if(statRestoreDelay > 0)
        {
            statRestoreDelay--;
        }
        if(drinkDelay > 0)
        {
            drinkDelay--;
        }
	if(skillDelay > 0)
	{
	    skillDelay--;
	}
        if(isDead && deathDelay >= 0)
        {
            deathDelay--;
            applyDead();
        }
        checkForUpdates();
        if(attackingNPC)
        {
            PlayerAttack pl = new PlayerAttack(this);
            pl.attackNPC();
            pl = null;
        }
        if(attackingPlayer)
        {
            PlayerAttack pl = new PlayerAttack(this);
            pl.attackPlayer();
            pl = null;
        }
        if(loadingDelay == 0)
        {
            main.ge.item.loadItems(this);
        }
        if(itemPickup)
        {
            PickupItem pi = new PickupItem(this);
            pi = null;
        }
        if(npcOption1)
        {
            NPCOption1 no1 = new NPCOption1(this);
            no1 = null;
        }
        if(npcOption2)
        {
            NPCOption2 no2 = new NPCOption2(this);
            no2 = null;
        }
        if(objectClick1)
        {
            ObjectClick1 oc1 = new ObjectClick1(this);
            oc1 = null;
        }
        if(objectClick2)
        {
            ObjectClick2 oc2 = new ObjectClick2(this);
            oc2 = null;
        }
        if(objectClick3)
        {
            ObjectClick3 oc3 = new ObjectClick3(this);
            oc3 = null;
        }
        if(saveGameDelay == 0)
        {
            main.ge.saveGame(this);
            saveGameDelay = 17;
        }
        if(updateShop)
        {
            updateShop = false;
            frame.resetShop(this, myShopId);
        }
        if(statRestoreDelay <= 0)
        {
            for(int i1 = 0; i1 < skillLvl.length; i1++)
            {
                int xpLvl = getLevelForXP(i1);
                if(skillLvl[i1] < xpLvl)
                {
                    skillLvl[i1]++;
                    frame.sendSkillLvl(this, i1);
                }
                else if(skillLvl[i1] > xpLvl)
                {
                    skillLvl[i1]--;
                    frame.sendSkillLvl(this, i1);
                }
            }
            statRestoreDelay = 47;
        }
    }

    private void loginSuccess()
    {
        isOnline = true;
	if(teleportToX <= 0 && teleportToY <= 0) {
		teleportToPoint(3100, 3957, 0);
	}
        appearanceUpdateRequired = true;
        updateRequired = true;
        frame.resetItems(this, 3214);
        for(int i = 0; i < frame.sidebars.length; i++)
        {
            frame.setSidebar(this, frame.sidebars[i], i);
        }
        for(int i = 0; i < skillLvl.length; i++)
        {
            frame.sendSkillLvl(this, i);
        }
        for(int i = 0; i < equipment.length; i++)
        {
            if(i == 6 || i == 8 || i == 11 || i > 11)
                continue;
            frame.setEquipment(this, equipment[i], equipmentN[i], i);
        }
        frame.setPlayerOption(this, "null", 1);
        frame.setPlayerOption(this, "Trade with", 2);
        frame.setPlayerOption(this, "Duel", 3);
        if(skillLvl[3] <= 0)
        {
            isDead = true;
            deathDelay = 2;
        }
    }

    public void initialize()
    {
        frame.loginFrame(this);
        updateP.update(this);
    }

    public void requestAnim(int animId, int animD)
    {
        animRequest = animId;
        animDelay = animD;
        animUpdateRequired = true;
        updateRequired = true;
    }

    public void requestGFX(int gfxId, int gfxD)
    {
        gfxRequest = gfxId;
        gfxDelay = gfxD;
        gfxUpdateRequired = true;
        updateRequired = true;
    }

    public void requestFaceTo(int faceId)
    {
        faceToRequest = faceId;
        faceToUpdateRequired = true;
        updateRequired = true;
    }

    public void calculateEquipmentBonus()
    {
        for(int i = 0; i < equipmentBonus.length; i++)
        {
            equipmentBonus[i] = 0;
        }
        for(int i = 0; i < equipment.length; i++)
        {
            if(equipment[i] > -1)
            {
                for(int j = 0; j < main.ge.item.getMaxListedItems(); j++)
                {
                    if(main.ge.item.itemLists[j] != null)
                    {
                        if(main.ge.item.itemLists[j].getItemId() == equipment[i])
                        {
                            for(int k = 0; k < equipmentBonus.length; k++)
                            {
                                equipmentBonus[k] += main.ge.item.itemLists[j].getBonuses()[k];
                            }
                            break;
                        }
                    }
                }
            }
        }
        writeBonus();
    }

    private void writeBonus()
    {
        int offset = 0;
        String send = "";
        for(int i = 0; i < equipmentBonus.length; i++)
        {
            if(equipmentBonus[i] >= 0)
            {
                send = (main.ge.item.bonusName[i] + ": +" + equipmentBonus[i]);
            }
            else
            {
                send = (main.ge.item.bonusName[i] + ": -" + Math.abs(equipmentBonus[i]));
            }
            if(i == 10)
            {
                offset = 1;
            }
            frame.setText126(this, send, (1675 + i + offset));
        }
    }

    public void viewShop(int shopId)
    {
        myShopId = shopId;
        frame.showInterface(this, 3824, 3822);
        frame.resetShop(this, shopId);
        frame.setText126(this, main.ge.shops.shopLists[shopId].shopName, 3901);
        frame.resetItems(this, 3823);
    }

    public int getLevelForXP(int skillId)
    {
        int exp = skillXP[skillId];
        int points = 0;
        int output = 0;
        for(int lvl = 1; lvl < 100; lvl++)
        {
            points += Math.floor((double)lvl + 300.0 * Math.pow(2.0, (double)lvl / 7.0));
            output = (int)Math.floor(points / 4);
            if((output - 1) >= exp)
            {
                return lvl;
            }
        }
        return 99;
    }

    public void addSkillXP(int amount, int skill)
    {
        if(skill > skillXP.length || skill < 0)
        {
            return;
        }
        int oldLevel = getLevelForXP(skill);
        skillXP[skill] += amount;
        int newLevel = getLevelForXP(skill);
        if(oldLevel < newLevel)
        {
            requestGFX(199, 6553600);
            LevelUp l = new LevelUp(this, skill);
            if(skill == 3)
            {
                updateHP(newLevel - oldLevel, true);
            }
            else
            {
                skillLvl[skill] += (newLevel - oldLevel);
            }
            totalLvl = (getLevelForXP(0) + getLevelForXP(1) + getLevelForXP(2) + getLevelForXP(3) + getLevelForXP(4) 
            + getLevelForXP(5) + getLevelForXP(6) + getLevelForXP(7) + getLevelForXP(8) + getLevelForXP(9) + getLevelForXP(10) 
            + getLevelForXP(11) + getLevelForXP(12) + getLevelForXP(13) + getLevelForXP(14) + getLevelForXP(15) + getLevelForXP(16) 
            + getLevelForXP(17) + getLevelForXP(18) + getLevelForXP(19) + getLevelForXP(20));
            totalXP = (skillXP[0] + skillXP[1] + skillXP[2] + skillXP[3] + skillXP[4] + skillXP[5] + skillXP[6] + skillXP[7] 
            + skillXP[8] + skillXP[9] + skillXP[10] + skillXP[11] + skillXP[12] + skillXP[13] + skillXP[14] + skillXP[15] 
            + skillXP[16] + skillXP[17] + skillXP[18] + skillXP[19] + skillXP[20]);
            frame.setText126(this, "Total Lvl: " + totalLvl, 3984);
            appearanceUpdateRequired = true;
            updateRequired = true;
            l = null;
        }
        frame.sendSkillLvl(this, skill);
    }

    public void updateHP(int chHp, boolean heal)
    {
        if(heal)
        {
            skillLvl[3] += chHp;
            if(skillLvl[3] > getLevelForXP(3))
            {
                skillLvl[3] = getLevelForXP(3);
            }
            subtractDamage(chHp);
        }
        else if(!heal)
        {
            skillLvl[3] -= chHp;
            if(skillLvl[3] <= 0)
            {
                skillLvl[3] = 0;
                isDead = true;
                deathDelay = 3;
            }
        }
        frame.setText126(this, skillLvl[3] + "", 4016);
    }

    public void appendHit(int damage, int posion)
    {
        if(damage > skillLvl[3])
        {
            damage = skillLvl[3];
        }
        updateHP(damage, false);
        hitDiff1 = damage;
        posionHit1 = posion;
        hit1UpdateRequired = true;
        updateRequired = true;
    }

    public void checkForUpdates()
    {
        if(isInWild != main.ge.wildernessArea(absX, absY))
        {
            isInWild = main.ge.wildernessArea(absX, absY);
            if(isInWild)
            {
                frame.setPlayerOption(this, "Attack", 1);
            }
            else
            {
                frame.setPlayerOption(this, "null", 1);
            }
        }
    }

    public void teleportToPoint(int x, int y, int height)
    {
        teleportToX = x;
        teleportToY = y;
        heightLevel = height;
        newWalkCmdSteps = 0;
        newWalkCmdX[0] = newWalkCmdY[0] = 0;
    }

    public void destruct()
    {
        teleportToX = teleportToY = absX = absY = 0;
        playerListSize = 0;
        npcListSize = 0;
        try
        {
            in.close();
            out.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        try
        {
            socket.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        inStream.destruct();
        outStream.destruct();
        in = null;
        out = null;
        socket = null;
        inStream = null;
        outStream = null;
        inStreamDecryption = null;
        equipment = null;
        equipmentN = null;
        walkingQueueX = null;
        walkingQueueY = null;
        travelBackX = null;
        travelBackY = null;
        newWalkCmdX = null;
        newWalkCmdY = null;
        playerList = null;
        playerInListBitmap = null;
        chatText = null;
        disconnected = null;
        items = null;
        itemsN = null;
        npcList = null;
        npcInListBitmap = null;
        bankItems = null;
        bankItemsN = null;
        killedBy = null;
    }
}